<?php
/**
 * Logger
 * 標準デバッグログ管理モジュール
 * Config.Debug.Levelを参照して動作を切り替える。
 * 
 * @since 2012/01/24
 */
class Logger {
	private $_log = ''; // ログ実態
	
	public function __construct () {
		Y('Module')->add ($this, __CLASS__);
		
		// トップレベルにLog作成
		// これのcallはModule.Loggerと同等
		$objTop = Y()->add ($this, 'Log');
		$Level = Y('Config.Debug.Level')->call();
		switch ($Level) {
			case '4' :
				$d = $objTop->add (Y::func($this, 'logDebug'), 'd');
			case '3' :
				$objTop->add (Y::func($this, 'logNotice'), 'n');
			case '2' :
				$objTop->add (Y::func($this, 'logWarning'), 'w');
			case '1' :
				$objTop->add (Y::func($this, 'logFatal'), 'f');
		}
		$this->_log.= '-----ログ取得開始-----'."\n";
		$this->_log.= '[Debug][global]処理開始時刻:'.date('Y-m-d H:i:s')."\n";
		
		// エラーハンドラの登録
		set_error_handler (array($this, 'errorHandler'));
		
		// 例外ハンドラの登録
		set_exception_handler(array($this, 'exceptionHandler'));
	}

	/**
	 * callは今のところ役割なし
	 * 
	 * @since 2012/01/24
	 */
	function call () {
	}
	
	/**
	 * デストラクタでログファイルを書き込む
	 * 
	 * @since 2012/01/24
	 */
	public function __destruct () {
		// VarsがなければVarsを呼び出す
		if (Y('Var')->isEmpty()) Y('Util.loadModule')->call ('Vars');
		Y('Log.d')->call('処理終了時刻:'.date('Y-m-d H:i:s'));
		$this->_log.='-----ログ取得終了-----'."\n";
		Y('Var.System.Add')->call(Y('Config.Debug.LogDir')->sput('', '%s/debug.log'), $this->_log);
	}

	/**
	 * デバッグレベルのログを出力する
	 * 
	 * @since 2012/01/24
	 * @param string $log ログ内容
	 */
	 public function logDebug ($log) {
	 	$this->addLog ('Debug', $log);
	 }

	/**
	 * ノーティスレベルのログを出力する
	 * 
	 * @since 2012/01/24
	 * @param string $log ログ内容
	 */
	 public function logNotice ($log) {
	 	$this->addLog ('Notice', $log);
	 }

	/**
	 * ワーニングレベルのログを出力する
	 * 
	 * @since 2012/01/24
	 * @param string $log ログ内容
	 */
	 public function logWarning ($log) {
	 	$this->addLog ('Warning', $log);
	 }

	/**
	 * エラーレベルのログを出力する
	 * 
	 * @since 2012/01/24
	 * @param string $log ログ内容
	 */
	 public function logFatal ($log) {
	 	$this->addLog ('Fatal', $log);
	 }
	 
	 /**
	  * 各エラー処理のサブルーチン
	  * 実際に記録する
	  * 
	  * @since 2012/01/24
	  * @param string $level DebugとかFatalとかのレベル
	  * @param string $log 実際のログ
	  */
	 private function addLog ($level, $log) {
	 	// セレクタを取得する
	 	if ($ref = YuTori::selected(1)) {
	 		$selector = $ref->getFullName ();
	 		if (!$selector) $selector = 'NON-STRING-SELECT';
	 	} else {
	 		$selector = 'GLOBAL';
	 	}
	 	
	 	$this->_log .= '['.$level.']['.$selector.']'.$log."\n";
	 }

	/**
	 * エラーハンドラ
	 * エラーが起こったとき自動でログファイルに追記する。
	 * 
	 * ほとんどの場合、E_ERROR等、スクリプト停止級のエラーは取得できないので注意。
	 * 
	 * @since 2012/01/24
	 */
	function errorHandler ($errno, $errstr, $errfile, $errline) {
		$log = '['.$errno.']'.$errstr.':'.$errfile.'('.$errline.'行)';
		switch ($errno) {
			case E_ERROR:
				$this->logFatal ($log);
				$this->__destruct();
				break;
			case E_WARNING:
				$this->logWarning ($log);
				break;
			case E_NOTICE:
				$this->logNotice ($log);
				break;
			default:
				$this->logDebug ($log);
				break;
		}
		
		// falseを指定するとPHPのデフォルトエラーハンドラにも処理が渡る。
		return false;
	}

	/**
	 * 例外ハンドラ
	 * キャッチされない例外が起こったとき自動でログファイルに追記する。
	 * 
	 * @since 2012/01/24
	 */
	function exceptionHandler ($exception) {
		$this->logFatal ('例外が発生しました。');
		$this->logFatal ($exception->getMessage());
		$this->__destruct();
	}

}

new Logger ();